package me.zhengjie.modules.workflow.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.exception.BadRequestException;
import me.zhengjie.modules.system.service.UserService;
import me.zhengjie.modules.system.service.dto.UserDto;
import me.zhengjie.modules.workflow.commad.ExecutionVariableDeleteCmd;
import me.zhengjie.modules.workflow.commad.FlowToFirstCmd;
import me.zhengjie.modules.workflow.commad.FlowToPreNodeCmd;
import me.zhengjie.modules.workflow.commad.TaskDeleteCmd;
import me.zhengjie.modules.workflow.service.WorkFlowService;
import me.zhengjie.utils.SecurityUtils;
import me.zhengjie.utils.StringUtils;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.*;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.identity.User;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentQuery;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.DelegationState;
import org.activiti.engine.task.Task;
import org.activiti.image.ProcessDiagramGenerator;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @description: WorkFlowServiceImpl <br>
 * @date: 2020/10/18 10:42 <br>
 * @author: wangz <br>
 * @version: 1.0 <br>
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class WorkFlowServiceImpl implements WorkFlowService {

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private ProcessEngine processEngine;

    @Autowired
    private UserService userService;

    @Autowired
    private IdentityService identityService;

    @Autowired
    private ManagementService managementService;

    /**
     * 任务处理人id
     */
    public static final String DEAL_USER_ID_KEY = "dealUserId";

    /**
     * 任务状态：未处理
     */
    public static final String DELEGATE_STATE = "PENDING";


    @Override
    @Transactional(rollbackFor = Exception.class)
    public String startWorkflow(String pdKey, String businessKey, Map<String, Object> variables) {
        //1.取得最新流程定义
        ProcessDefinition processDef = getLatestProcDef(pdKey);

        /**
         * 如果库中又有此流程最新版本，重新加载流程文件并部署流程
         * pdKey：流程key，必须要求与文件名称一致
         */
        if (processDef == null) {
            //2.部署流程
            processEngine.getRepositoryService()
                    .createDeployment()//创建部署对象
                    .name(pdKey)
                    .addClasspathResource("processes/" + pdKey + ".bpmn")
                    .deploy();
            processDef = getLatestProcDef(pdKey);
        }

        /**
         * 取得流程的一个实例，并返回流程实例id
         * 启动流程时，可以设置相关参数，比如业务外键，相关管理模型外键或者表单KEY
         */
        ProcessInstance process = runtimeService.startProcessInstanceById(processDef.getId(), businessKey, variables);
        //TODO 设置流程实例相关任务变量，比如任务到期时间，过期天数，任务类型等，设计一个业务表，记录所有相关变量
        //1、通过流程部署ID，找到部署信息
        Deployment deployment = repositoryService
                .createDeploymentQuery()
                .deploymentId(processDef.getDeploymentId())
                .singleResult();
        //2、通过部署信息，找到模型定义
        Model model = repositoryService
                .createModelQuery()
                .modelName(deployment.getName())
                .modelKey(deployment.getKey())
                .latestVersion()
                .singleResult();

        //一个模型可能要关联很多表单，
        // 需要在系统中控制住，
        // 如果关联了一个表单，这其他表单不能再关联

        return process.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void continueWorkflow(String taskId, Map variables) {
        /**
         * 取得任务，判断当前委托任务状态是否是未处理的任务
         */
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        //获取当前用户
        UserDto user = userService.findById(SecurityUtils.getCurrentUserId());
        // 判断当前用户是否能够办理此任务权限
        validateAuthority(user, task);
        DelegationState delegationState = task.getDelegationState();
        if (delegationState != null && DELEGATE_STATE.equals(delegationState.toString())) {
            // 委托任务，先需要被委派人处理完成任务
            taskService.resolveTask(taskId, variables);
        } else {
            //当前任务被当前受理人签收
            String dealUserId = variables.get(DEAL_USER_ID_KEY).toString();
            taskService.claim(taskId, dealUserId);
        }

        // 先完成任务看下
        taskService.complete(taskId);

        // 根据，办理界面点击的状态，设置
//        String status = (String) taskService.getVariable(task.getId(), "status");
//        //如果是流程拒绝，发起人重新发起
//        if ("reject".equals(status)) {
//            // 重新发起任务
//            this.rollbackFirstask(task.getId(),String.valueOf( user.getId()));
//        } else {
//            // 设置参数
//            taskService.setVariables(taskId, variables);
//            // 完成
//            taskService.complete(taskId);
//        }
    }

    /**
     * 验证用户是否有权限审批此任务
     *
     * @param user
     * @param task
     * @return
     */
    private void validateAuthority(UserDto user, Task task) {
        String userId = String.valueOf(user.getId());
        //如果是指定了审批人，判断是否一致
        if (StringUtils.isNotEmpty(task.getAssignee())) {
            if (!userId.equals(task.getAssignee())) {
                throw new BadRequestException("当前节点已被【" + userService.findUserNameById(task.getAssignee()) + "】签收，您无权限办理该任务！");
            }
        } else {
            List<FlowElement> flowElementList = getThisNodeByInsId(task.getProcessInstanceId());
            //遍历所有节点
            for (FlowElement flowElement : flowElementList) {
                List<String> candidateGroups = ((UserTask) flowElement).getCandidateGroups();
                if (candidateGroups.size() == 0) {
                    return;
                }
                //遍历所有组
                for (String group : candidateGroups) {
                    List<User> userList = identityService.createUserQuery().memberOfGroup(group).list();
                    for (User u : userList) {
                        //匹配上
                        if (u.getId().equals(user.getUsername())) {
                            return;
                        }
                    }
                }
            }
            throw new BadRequestException("您无权限审批此任务！");
        }
    }

    /**
     * 获取节点通过流程实例ID
     *
     * @param processInstanceId
     * @return
     */
    private List<FlowElement> getThisNodeByInsId(String processInstanceId) {
        List<FlowElement> list = null;
        BpmnModel model = repositoryService.getBpmnModel(processInstanceId);
        if (model != null) {
            list = (List) model.getMainProcess().getFlowElements();
        }
        return list;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delegateWorkflow(String taskId, Map variables) {
        //将任务节点委托给受委托人
        String dealUserId = variables.get(DEAL_USER_ID_KEY).toString();
        taskService.delegateTask(taskId, dealUserId);
        //被委派人办理任务后（任务回到委派人）
        //taskService.resolveTask(taskId,variables);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void endWorkflow(String pProcessInstanceId, String deleteReason) {
        // 结束流程
        runtimeService.deleteProcessInstance(pProcessInstanceId, deleteReason);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String getCurrentTask(String taskId) {
        //获取当前任务
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .active().singleResult();
        return task.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<String> findUserProcessIds(String userId, String pdKey, Integer pageNo, Integer pageSize) {
        List<Task> resultTask;
        if (pageSize == 0) {
            // 不分页
            resultTask = taskService.createTaskQuery().processDefinitionKey(pdKey)
                    .taskCandidateOrAssigned(userId).list();
        } else {
            resultTask = taskService.createTaskQuery().processDefinitionKey(pdKey)
                    .taskCandidateOrAssigned(userId).listPage(pageNo - 1, pageSize);
        }
        //根据流程实例ID集合
        List<String> processInstanceIds = resultTask.stream()
                .map(task -> task.getProcessInstanceId())
                .collect(Collectors.toList());
        return processInstanceIds == null ? new ArrayList<>() : processInstanceIds;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void getProcessImage(String pProcessInstanceId, HttpServletResponse response) {
        log.info("[开始]-获取流程图图像");
        // 设置页面不缓存
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        response.setContentType("image/png");
        InputStream imageStream = null;
        try (OutputStream os = response.getOutputStream()) {
            //  获取历史流程实例
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(pProcessInstanceId).singleResult();

            if (historicProcessInstance == null) {
                throw new BadRequestException("获取流程实例ID[" + pProcessInstanceId + "]对应的历史流程实例失败！");
            } else {
                // 获取流程历史中已执行节点，并按照节点在流程中执行先后顺序排序
                List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery()
                        .processInstanceId(pProcessInstanceId).orderByHistoricActivityInstanceId().asc().list();

                // 已执行的节点ID集合
                List<String> executedActivityIdList = new ArrayList<String>();
                int index = 1;
                log.info("获取已经执行的节点ID");
                for (HistoricActivityInstance activityInstance : historicActivityInstanceList) {
                    executedActivityIdList.add(activityInstance.getActivityId());

                    log.info("第[" + index + "]个已执行节点=" + activityInstance.getActivityId() + " : " + activityInstance.getActivityName());
                    index++;
                }
                // 获取流程定义
                BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());

                // 已执行的线集合
                List<String> flowIds = getHighLightedFlows(bpmnModel, historicActivityInstanceList);

                // 流程图生成器
                ProcessDiagramGenerator pec = processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator();
                // 获取流程图图像字符流（png/jpg）
                imageStream = pec.generateDiagram(bpmnModel, "jpg", executedActivityIdList, flowIds, "宋体", "微软雅黑", "黑体", null, 2.0);

                try {
                    //读取流中的数据，bytes
                    byte[] bytes = new byte[imageStream.available()];
                    imageStream.read(bytes);
                    imageStream.close();

                    //输出文件流
                    response.setContentType("image/*");
                    os.write(bytes);
                    os.close();
                } catch (IOException e) {
                    throw new BadRequestException("查看流程图发生异常");
                }


                int bytesRead = 0;
                byte[] buffer = new byte[8192];
                while ((bytesRead = imageStream.read(buffer, 0, 8192)) != -1) {
                    os.write(buffer, 0, bytesRead);
                }

            }
            log.info("[完成]-获取流程图图像");
        } catch (Exception e) {
            log.error("【异常】-获取流程图失败！", e);
        } finally {
            if (imageStream != null) {
                try {
                    imageStream.close();
                } catch (IOException e) {
                    log.error("关闭流异常：", e);
                }
            }
        }
    }


    @Override
    public List<String> getHighLightedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) {
        // 24小时制
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // 用以保存高亮的线flowId
        List<String> highFlows = new ArrayList<String>();

        for (int i = 0; i < historicActivityInstances.size() - 1; i++) {
            // 对历史流程节点进行遍历
            // 得到节点定义的详细信息
            FlowNode activityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(i).getActivityId());

            // 用以保存后续开始时间相同的节点
            List<FlowNode> sameStartTimeNodes = new ArrayList<FlowNode>();
            FlowNode sameActivityImpl1 = null;
            // 第一个节点
            HistoricActivityInstance activityImpl_ = historicActivityInstances.get(i);
            HistoricActivityInstance activityImp2_;

            for (int k = i + 1; k <= historicActivityInstances.size() - 1; k++) {
                // 后续第1个节点
                activityImp2_ = historicActivityInstances.get(k);

                if (activityImpl_.getActivityType().equals("userTask") && activityImp2_.getActivityType().equals("userTask") &&
                        df.format(activityImpl_.getStartTime()).equals(df.format(activityImp2_.getStartTime()))) {
                    // 都是usertask，且主节点与后续节点的开始时间相同，说明不是真实的后继节点
                } else {
                    //找到紧跟在后面的一个节点
                    sameActivityImpl1 = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(k).getActivityId());
                    break;
                }

            }
            // 将后面第一个节点放在时间相同节点的集合里
            sameStartTimeNodes.add(sameActivityImpl1);
            for (int j = i + 1; j < historicActivityInstances.size() - 1; j++) {
                // 后续第一个节点
                HistoricActivityInstance activityImpl1 = historicActivityInstances.get(j);
                // 后续第二个节点
                HistoricActivityInstance activityImpl2 = historicActivityInstances.get(j + 1);

                if (df.format(activityImpl1.getStartTime()).equals(df.format(activityImpl2.getStartTime()))) {
                    // 如果第一个节点和第二个节点开始时间相同保存
                    FlowNode sameActivityImpl2 = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityImpl2.getActivityId());
                    sameStartTimeNodes.add(sameActivityImpl2);
                } else {// 有不相同跳出循环
                    break;
                }
            }
            // 取出节点的所有出去的线
            List<SequenceFlow> pvmTransitions = activityImpl.getOutgoingFlows();
            // 对所有的线进行遍历
            for (SequenceFlow pvmTransition : pvmTransitions) {
                // 如果取出的线的目标节点存在时间相同的节点里，保存该线的id，进行高亮显示
                FlowNode pvmActivityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(pvmTransition.getTargetRef());
                if (sameStartTimeNodes.contains(pvmActivityImpl)) {
                    highFlows.add(pvmTransition.getId());
                }
            }

        }
        return highFlows;
    }

    /**
     * 获取最新版本流程
     *
     * @param pdKey
     * @return
     */
    private ProcessDefinition getLatestProcDef(String pdKey) {
        return repositoryService.createProcessDefinitionQuery().processDefinitionKey(pdKey).
                latestVersion().singleResult();
    }

    /**
     * 根据部署id获取到最新流程定义
     *
     * @param id
     * @return
     */
    private ProcessDefinition getLatestProcDefByDeployId(String id) {
        return repositoryService.createProcessDefinitionQuery()
                .deploymentId(id).latestVersion().singleResult();
    }

    @Override
    public void deploy(String id) {

        //1、获取流程
        Model model = repositoryService.getModel(id);
        if (model == null) {
            throw new BadRequestException("流程不存在，无法部署。");
        }

        try {
            //2、获取流程信息
            ObjectNode objectNode = (ObjectNode) new ObjectMapper().readTree(repositoryService.getModelEditorSource(model.getId()));
            BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(objectNode);

            String processName = model.getName() + ".bpmn20.xml";
            byte[] bytes = new BpmnXMLConverter().convertToXML(bpmnModel);
            //3、部署流程
            Deployment deployment = repositoryService
                    .createDeployment()
                    .name(model.getName())
                    .key(model.getKey())
                    .category(model.getCategory())
                    .addString(processName, new String(bytes, "UTF-8"))
                    .enableDuplicateFiltering().deploy();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

    @Override
    public void viewImage(String pdId, HttpServletResponse response) {
        //最新流程定义
        ProcessDefinition latestProcDef = repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionId(pdId)
                .latestVersion().singleResult();
        if (latestProcDef != null) {
            InputStream diagram = repositoryService.getProcessDiagram(latestProcDef.getId());
            try {
                //读取流中的数据，bytes
                byte[] bytes = new byte[diagram.available()];
                diagram.read(bytes);
                diagram.close();

                //输出文件流
                OutputStream os = response.getOutputStream();
                response.setContentType("image/*");
                os.write(bytes);
                os.close();
            } catch (IOException e) {
                throw new BadRequestException("查看流程图发生异常");
            }

        } else {
            throw new BadRequestException("流程定义不存在");
        }
    }

    @Override
    public void downloadBpmn(String mid, HttpServletResponse response) {
        try {
            Model modelData = repositoryService.getModel(mid);
            BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
            JsonNode editorNode = new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
            BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
            BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
            byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);

            ByteArrayInputStream in = new ByteArrayInputStream(bpmnBytes);
            IOUtils.copy(in, response.getOutputStream());
            String filename = bpmnModel.getMainProcess().getId() + ".bpmn20.xml";
            response.setHeader("Access-Control-Expose-Headers", "*");
            response.setHeader("Content-Disposition", "attachment; filename=" + filename);
            response.flushBuffer();

        } catch (IOException e) {
            throw new BadRequestException("导出流程bpmn文件错误");
        }
    }

    @Override
    public void signFor(String taskId, Map param) {
        //依据任务ID查询到任务
        Task task = taskService.createTaskQuery().taskId(taskId).active().singleResult();
        Long currentUserId = SecurityUtils.getCurrentUserId();
        UserDto dto = userService.findById(currentUserId);
        if (StringUtils.isNotEmpty(task.getAssignee())
                && !task.getAssignee().equals(dto.getId())) {
            throw new BadRequestException("此任务已经被【" + task.getName() + "】签收");
        }
        //完成签收
        try {
            DelegationState delegationState = task.getDelegationState();
            if (delegationState != null && DELEGATE_STATE.equals(delegationState.toString())) {
                // 委托任务，先需要被委派人处理完成任务
                taskService.resolveTask(taskId, param);
            } else {
                // 当前用户签收当前任务
                //String dealUserId = param.get(DEAL_USER_ID_KEY).toString();
                taskService.claim(taskId, String.valueOf(currentUserId));
            }
        } catch (Exception e) {
            UserDto userDto = userService.findById(Long.parseLong(task.getAssignee()));
            throw new BadRequestException("此任务已经被【" + userDto.getUsername() + "】签收");
        }

    }

    @Override
    public void viewProcessTrace(String pdId, HttpServletResponse response) {

    }

    @Override
    public void rollBack(String taskId) {
        //1、获取任务
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        //执行命令
        managementService.executeCommand(new ExecutionVariableDeleteCmd(task.getExecutionId()));
        managementService.executeCommand(new FlowToPreNodeCmd(task));
        managementService.executeCommand(new TaskDeleteCmd(taskId));
    }

    @Override
    public void deployResource(MultipartFile file) {
        //上传打包好的文件，比如zip文件，以及单个
        try {
            repositoryService.createDeployment()
                    .addInputStream(file.getOriginalFilename(), file.getInputStream())
                    .name(file.getOriginalFilename())
                    .key(file.getOriginalFilename())
                    .deploy()
                    .getId();
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("upload failed");
        }
    }

    @Override
    public void reject(String taskId, String user) {
        //删除所有当前task，保留一个，并且将该task的审批人设为发起人
        Task t = taskService.createTaskQuery()
                .taskId(taskId)
                .singleResult();
        //获取到任务相关的流程实例ID
        String instanceId = t.getProcessInstanceId();

        List<Task> tasks = taskService.createTaskQuery()
                .processInstanceId(instanceId)
                .list();
        //获取一个任务，删除一些任务
        Task luckyTask = tasks.get(0);
        managementService.executeCommand(new ExecutionVariableDeleteCmd(t.getExecutionId()));
        for (int i = 1; i < tasks.size(); ++i) {
            managementService.executeCommand(new TaskDeleteCmd(tasks.get(i).getId()));
            managementService.executeCommand(new ExecutionVariableDeleteCmd(tasks.get(i).getExecutionId()));
        }
        //将发起人设置为当前审批人
        taskService.setAssignee(luckyTask.getId(), (String) taskService.getVariable(luckyTask.getId(), "submitter"));
        //设置变量标识当前状态是已拒绝状态
        taskService.setVariable(luckyTask.getId(), "status", "reject");
    }

    @Override
    public void rollbackFirstask(String taskId, String user) {
        //移除标记REJECT的status
        taskService.removeVariable(taskId, "status");
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        //删除任务
        managementService.executeCommand(new TaskDeleteCmd(taskId));
        //删除变量
        managementService.executeCommand(new ExecutionVariableDeleteCmd(task.getExecutionId()));
        //将流程回滚到第一个节点
        managementService.executeCommand(new FlowToFirstCmd(task));
    }

    @Override
    public void reassignment(String processInstanceId,String roleId, String userId) {
        // 查询该流程实例已被激活的任务，一个流程启动之后，产生两个以上执行流，同时act_ru_task 插入当前运行的任务节点
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).active().singleResult();
        Map<String,Object> variables=new HashMap<String,Object>();
        variables.put("approve", "false");
        //一定要认领任务之后，重新获取当前任务，然后分配角色。
        taskService.claim(task.getId(), userId);
        taskService.complete(task.getId(), variables);
        task = taskService.createTaskQuery().processInstanceId(processInstanceId).active().singleResult();

        //指派任务
        taskService.addCandidateUser(task.getId(),userId);
        //如果是指派角色，指定候选人
        taskService.addCandidateGroup(task.getId(),roleId);
    }

    @Override
    public void inquire(String taskId,List<String> users,String user) {
        //征询参数
        Map<String, Object> taskParams = new HashMap<>();
        taskParams.put("outcome", "inquire");
        taskParams.put("approveUser", user);

        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        //执行重新分配操作
        taskService.setAssignee(task.getId(), users.get(0));
        //标识状态为征询，需要记录在本地变量中
        taskService.setVariableLocal(task.getId(), "status", "inquire");
        //记录征询人信息
        taskService.setVariableLocal(task.getId(), "originUser", user);
        Task t = taskService.createTaskQuery()
                .taskId(task.getId())
                .singleResult();
    }

    @Override
    public void reply(String taskId, String toUser, String user) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (toUser == null) {
            toUser = (String) taskService.getVariableLocal(task.getId(), "originUser");
        }
        Map<String, Object> taskParams = new HashMap<>();
        taskParams.put("outcome", "reply");
        taskParams.put("approveUser", user);

        //执行重新分配操作
        taskService.setAssignee(task.getId(), toUser);
        taskService.removeVariable(task.getId(), "status");
        taskService.removeVariable(task.getId(), "originUser");
        Task t = taskService.createTaskQuery()
                .taskId(task.getId())
                .singleResult();
    }

    @Override
    public void transfer(String taskId, String userId) {
        // 当前用户id，应该是当前用户拥有的任务
        Long currentUserId = SecurityUtils.getCurrentUserId();
        /**
         * 删除指派人重新指派
         */
        taskService.deleteCandidateUser(taskId,String.valueOf(currentUserId));
        taskService.addCandidateUser(taskId,userId);
        // 设置owner字段，以便后续查询指派给他人的任务
        taskService.setOwner(taskId, String.valueOf(currentUserId));
    }


    /**
     * 获取流程最新部署信息
     *
     * @param model
     * @return
     */
    private Deployment getLatestDeploy(Model model) {
        //部署查询对象
        DeploymentQuery query = processEngine.getRepositoryService().createDeploymentQuery();
        Deployment deployment = query.deploymentName(model.getName())
                .deploymentKey(model.getKey()).latest().singleResult();
        return deployment;
    }
}
